package engine;


import java.util.LinkedList;
import java.util.Queue;
import views.GamePlayView;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.drawable.PictureDrawable;
import android.util.Log;
import android.widget.Toast;

import com.tetris.project.ShowScores;
import com.tetris.project.Shr_Res;
import framework.ScreenObject;
import framework.interfaces.Drawable;
import framework.interfaces.Updatable;
import framework.interfaces.Movable.Side;
import framework.utils.GlobalManager;

public class Board implements Drawable, Updatable{

	private int[][] mBoard; // here We will save the board status at any time
	public static int BOARD_ROWS; // Game Board Rows ()
	public static int BOARD_COLS; // Game Board Columns ()
	static final char COLOR_MASK = 0x0F;
	static final char ROWS_MASK = 0xFF00;
	static final char COLS_MASK = 0xFF;
	static final int POSITION_SHIFT = 8;
	static final char COLLISION_DOWN = 0x1;
	static final char COLLISION_RIGHT = 0x2;
	static final char COLLISION_LEFT = 0x4;
	static final double TWO_LINE_FACTOR = 1.4;
	static final double THREE_LINE_FACTOR = 1.6;
	static final double FOUR_LINE_FACTOR = 1.8;
	static final double BLOCK_FACTOR = 1.8;
	private int pixelsLeftLeft = 0;
	private int pixelsLeftRight = 0;
	public static int score;
	PictureDrawable mBoardDrawable;
	Picture mBoardPicture;
	int pieceUnitWidth;
	int pieceUnitHeight;
	Block color_block;

	public Board()
	{
		Log.d(Shr_Res.TAG, "Board() mWindowHeight = "+GamePlayView.mWindowHeight);
		Log.d(Shr_Res.TAG, "Board() mWindowWidth = "+GamePlayView.mWindowWidth);
		Log.d(Shr_Res.TAG, "Board() BOARD_ROWS = "+BOARD_ROWS);
		Log.d(Shr_Res.TAG, "Board() BOARD_COLS = "+BOARD_COLS);
		mBoardPicture = new Picture();
		mBoardDrawable =  new PictureDrawable(mBoardPicture);
		color_block= new Block();
		score = 0;
	}

	public boolean hasCollide(Piece piece, Side side)
	{
		char[] p = piece.getPiece();
		int y, x,
			bx = piece.getBoardX(),
			by =piece.getBoardY();

		for(int i = 0; i < TetrisPieceManager.NUMBER_BLOCKS_PER_PIECE; i++) {
			if (TetrisPieceManager.next != 6) {
				y =(((int)p[i]) / TetrisPieceManager.GRID_RESOLUTION);
				x =(((int)p[i]) % TetrisPieceManager.GRID_RESOLUTION);
				//Log.d(Shr_Res.TAG, "by + y = " +(by + y) + " bx + x = " +(bx + x));
				switch(side) {
				case DOWN:
					//Log.d(Shr_Res.TAG, "Looking a DOWN colision mBoard["
					//		+(by + y) + "]" + "[" + (bx + x) + "]");
					if(mBoard[by + y + 1][bx + x]!= 0)
						return true;
					break;
				case RIGHT:
					if ((bx + x) < (BOARD_COLS -1) )
						if(mBoard[by + y][bx + x + 1]!= 0)
							return true;
					break;
				case LEFT:
					if ((bx + x) > 0)
						if(mBoard[by + y][bx + x -1]!= 0)
							return true;
				}
			} else {
				switch(side) {
				case DOWN:
					while(Piece.isRotateRunning) {}
					if (Piece.mCurrent == 0 || Piece.mCurrent == 2) {// "|"
						if (mBoard[by + 4][bx] != 0) {
							return true;
						} else {
							return false;
						}
					} else {
						//Log.d(Shr_Res.TAG, "The value of bx is: = " + bx);
						//Log.d(Shr_Res.TAG, "Looking for Down collide, by + 1 == " + (by + 1)
						//	+ "bx + i = " + (bx  + i));
						if (mBoard[by + 1][bx + i] != 0)
							return true;
					}
					break;
				case RIGHT:
					if (Piece.mCurrent == 0 || Piece.mCurrent == 2) {// "|"
						if ((bx + 1) >= BOARD_COLS || mBoard[by + i][bx + 1] != 0)
							return true;
					} else {
						if ((bx + 4) >= BOARD_COLS || mBoard[by][bx + 4] != 0)
							return true;
					}
					break;
				case LEFT:
					if (Piece.mCurrent == 0 || Piece.mCurrent == 2) {// "|"
						if ((bx - 1) < 0 || mBoard[by + i][bx - 1] != 0)
							return true;
					} else {
						if ((bx - 1) < 0 || mBoard[by][bx - 1] != 0)
							return true;
					}
				}
			}
		}
		return false;
	}

	public void insertPiece(Piece piece)
	{
		//Insert the piece in the board and the proper indexes
		char[] p = piece.getPiece();
		int x, y,
			bx = piece.getBoardX(),
			by = piece.getBoardY(),
			resource = piece.getResource();
		int lines = 0, line_row = ~0;

		Queue<Point> piece_elements = new LinkedList<Point>();

		if (TetrisPieceManager.next != 6) {
			for(int i = 0; i < p.length; i++) {
				y =(((int)p[i]) / TetrisPieceManager.GRID_RESOLUTION);
				x =(((int)p[i]) % TetrisPieceManager.GRID_RESOLUTION);
				mBoard[by + y][bx + x] = resource;
				piece_elements.add(new Point(bx+x, by+y));
				Log.i("BOARD>INSERTPIECE", "BX:" + bx + ", X: " + x + ", BY:" + by + ", Y:"
						+ y + ", Reseource Location into memory: 0x"
						+ Integer.toHexString(resource & 0xFFFFFFFF));
			}
		} else {
			if (Piece.mCurrent == 0 || Piece.mCurrent == 2) {
				for(int i =0; i < 4; i++) {
					mBoard[by + i][bx] = resource;
					piece_elements.add(new Point(bx, by + i));
					Log.i("BOARD>INSERTPIECE", "BX:" + bx + ", BY: " + (by + i)
						+ ", Reseource Location into memory: 0x"
						+ Integer.toHexString(resource & 0xFFFFFFFF));
				}
			} else {
				for(int i =0; i < 4; i++) {
					mBoard[by][bx + i] = resource;
					piece_elements.add(new Point(bx + i, by));
					Log.i("BOARD>INSERTPIECE", "BX:" + (bx + i) + ", BY: " + by
						+ ", Reseource Location into memory: 0x"
						+ Integer.toHexString(resource & 0xFFFFFFFF));
				}
			}
		}

		Point origin = new Point();
		while(piece_elements.peek()!= null) {
			origin = piece_elements.remove();
			//Log.i("------- BLOCK ------","LOOP");

			if(check4Block(origin.x, origin.y, piece.getResource())) {
				eraseBlock();
				updateScore();
				organizeNewBoard(false, origin);
				check4Line();
				//break;
			}

			if(check4Line(origin.y,piece.getResource())== true){
				//Log.i("------- BLOCK ------","FOUND A LINE at " + (origin.y) );
				if ( line_row != origin.y){
					lines++;
					line_row=origin.y;
				}
				eraseLine(origin);
				organizeNewBoard(true, origin);
				check4Line();
			}
		}
		updateScore(lines);
		check4Line();
		//Log.i("------- SCORE ------"," SCORE: " + score );
		updateBoard();
		//printTableStatus();
	}

	protected synchronized void updateBoard() // Here is recorded the entire table.
	{
		synchronized(mBoardDrawable){
			Canvas c = mBoardDrawable.getPicture().beginRecording(ScreenObject.ImageResolution * BOARD_COLS,
					ScreenObject.ImageResolution * BOARD_ROWS);
			c.drawColor(Color.TRANSPARENT);
			int left, top;
			android.graphics.drawable.Drawable drawable;
			for (int i = 0; i < BOARD_ROWS -1; i++) {
				for (int j = 0; j < BOARD_COLS; j++) {
					if (mBoard[i][j] != 0) {
						drawable  = GlobalManager.DrawablesManager.getResource(mBoard[i][j]);
						left = j * ScreenObject.ImageResolution;
						top = i * ScreenObject.ImageResolution;
						drawable.setBounds( left, top, left + ScreenObject.ImageResolution , top + ScreenObject.ImageResolution);
						drawable.draw(c);
					}
				}
			}
			mBoardDrawable.getPicture().endRecording();
		}
	}

	protected boolean check4Block(int x, int y, int ID) {
		int i_rowBoundary, i_colBoundary;
		color_block.setSize(1,1);
		Point node = new Point();

		if (BOARD_ROWS - 2 == y) {
			node.y = --y;
			i_rowBoundary = 2;
		} else if (BOARD_ROWS -1 == y) {
			node.y = y-2;
			i_rowBoundary = 1;
		} else if ( y == 1) {
			node.y = y;
			i_rowBoundary = 2;
		} else if ( y == 0 ) {
			node.y = y;
			i_rowBoundary = 1;
		} else {
			node.y = y;
			i_rowBoundary = 3;
		}

		if ( BOARD_COLS - 2 == x) {
			node.x = x-2;
			i_colBoundary = 2;
		} else if (BOARD_COLS -1 == x) {
			node.x = x-2;
			i_colBoundary = 1;
		} else if ( x == 1) {
			node.x = x-1;
			i_colBoundary = 2;
		} else if ( x == 0 ) {
			node.x = x;
			i_colBoundary = 1;
		} else {
			node.x = x -2;
			i_colBoundary = 3;
		}

		for(int j = 0; j < i_rowBoundary; j++){
			for( int i = 0; i < i_colBoundary ; i++) {
				if( mBoard[node.y-j][node.x+i] == ID && mBoard[node.y-j][node.x+i+1] == ID && mBoard[node.y-j][node.x+i+2] == ID
						&& mBoard[node.y+1-j][node.x+i] == ID && mBoard[node.y+1-j][node.x+i+1] == ID && mBoard[node.y+1-j][node.x+i+2] == ID	
						&& mBoard[node.y+2-j][node.x+i] == ID && mBoard[node.y+2-j][node.x+i+1] == ID && mBoard[node.y+2-j][node.x+i+2] == ID
				){
					color_block.setSize(3,3);
					color_block.setOrigin(node.x+i, node.y-j);
				}
				else
					color_block.setSize(1,1);

				if (color_block.calculateSize() == 9){
					Log.i("------- BLOCK ------","FOUND A BLOCK");
					Shr_Res.blockWasForm = true;
					return true;
				}
			}
		}
		return false;
	}

	protected boolean check4Line(int row, int ID){
		if (row > (BOARD_ROWS - 1))
			return false;

		for (int i = 0; i < BOARD_COLS; i++)
			if ( mBoard[row][i] == 0)
				return false;
		return true;
	}

	protected void check4Line() {
		int lines = 0;
		boolean isLine;

		for(int i = BOARD_ROWS - 2; i > 0; i--) {
			isLine=true;
			for(int j = 0; j < BOARD_COLS; j++) {
				if (mBoard[i][j] != 0) {
					continue;
				} else {
					isLine=false;
					break;
				}
			}
			if(isLine) {
				//Log.i("------- BLOCK ------","FOUND A LINE at " + (i) );
				lines++;
				eraseLine(new Point(0,i));
				organizeNewBoard(true, new Point(0, i));
			}
		}
		score += 500 * lines;
	}

	protected void updateScore(int numLines){
		switch(numLines){
		case(0):
			break;
		case(1):
			score = (int)(score+500);
			break;
		case(2):
			score = (int)(score+(1000*TWO_LINE_FACTOR));
			break;
		case(3):
			score = (int)(score+(1500*THREE_LINE_FACTOR));
			break;
		case (4):
			score = (int)(score+(2000*FOUR_LINE_FACTOR));
		}
	}

	protected void updateScore(){
		score = (int)(score+(2000*BLOCK_FACTOR));
	}

	@Override
	public synchronized void draw(Canvas canvas) {
		synchronized (mBoardDrawable) {
			mBoardDrawable.setBounds(pixelsLeftRight, pixelsLeftLeft-2, ScreenObject.ImageResolution * BOARD_COLS,
				ScreenObject.ImageResolution * BOARD_ROWS);
			mBoardDrawable.draw(canvas);
		}
	}

	@Override
	public void update(double value) {
		// TODO Auto-generated method stub
	}
	
	public void definePieceUnitSize(int width, int height){
		pieceUnitWidth = width;
		pieceUnitHeight = height;
		Log.d(Shr_Res.TAG, "Unit Piece Width ="+pieceUnitWidth);
		Log.d(Shr_Res.TAG, "Unit Piece Height ="+pieceUnitHeight);
	}

	public void defineBoardSize(){
		Log.d(Shr_Res.TAG, "defineBoardSize() mWindowHeight = "+GamePlayView.mWindowHeight);
		Log.d(Shr_Res.TAG, "defineBoardSize() mWindowWidth = "+GamePlayView.mWindowWidth);
		int[][] mBoardPortrait;
		int[][] mBoardLandscape;

		if(GamePlayView.mWindowHeight > GamePlayView.mWindowWidth){  //means the screen is in portrait mode
			BOARD_ROWS = GamePlayView.mWindowHeight/pieceUnitHeight+1;
			pixelsLeftLeft = GamePlayView.mWindowHeight%pieceUnitHeight;
			Log.d(Shr_Res.TAG, "Pixels left height= "+pixelsLeftLeft);
			BOARD_COLS = (GamePlayView.mWindowWidth/3)*2/pieceUnitWidth;
			pixelsLeftRight = (GamePlayView.mWindowWidth%3);
			Log.d(Shr_Res.TAG, "Pixels left = width"+pixelsLeftRight);
			mBoardPortrait = new int[BOARD_ROWS][BOARD_COLS];
			mBoard = mBoardPortrait;
			Shr_Res.screenOrientation = true; /* Means portrait */
		} else {  //means the screen is in landscape mode
			BOARD_ROWS = GamePlayView.mWindowHeight/pieceUnitHeight+1;
			pixelsLeftLeft = GamePlayView.mWindowHeight%pieceUnitHeight;
			Log.d(Shr_Res.TAG, "Pixels left height= "+pixelsLeftLeft);
			BOARD_COLS = (GamePlayView.mWindowWidth/4)*3/pieceUnitWidth;
			pixelsLeftRight = (GamePlayView.mWindowWidth%3);
			Log.d(Shr_Res.TAG, "Pixels left = width"+pixelsLeftRight);
			mBoardLandscape = new int[BOARD_ROWS][BOARD_COLS];
			mBoard = mBoardLandscape;
			Shr_Res.screenOrientation = false; /* Landscape */
		}

		for(int i = 0 ; i < BOARD_COLS; i++)
			mBoard[BOARD_ROWS - 1][i] = 1;

		/* We can wake up the thread and print the table */
		//printTableStatus ();

		Log.d(Shr_Res.TAG, "ScreenOrientation = " + (Shr_Res.screenOrientation ? "Portrait" : "Landscape"));
		Log.d(Shr_Res.TAG, "defineBoardSize() BOARD_ROWS = " +BOARD_ROWS);
		Log.d(Shr_Res.TAG, "defineBoardSize() BOARD_COLS = "+BOARD_COLS);
	}

	public synchronized void printTableStatus () {
		Shr_Res.print = true;
	}

	public int[][] getBoard()
	{
		return mBoard;
	}

	//nested class that abstracts blocks of pieces
	private class Block {
		public Point origin, size;
		
		public Block(){
			origin = new Point(0,0);
			size = new Point(1,1);
				
		}

		public void setOrigin(int x, int y){
			origin.x =x;
			origin.y =y;
		}
		
		public void setSize(int x, int y){
			size.x =x;
			size.y =y;
		}
		
		public Point getOrigin(){
			return origin;
		}
		
		public int calculateSize(){
			return size.x*size.y;
		}
	}

	private void eraseBlock()
	{
		for (int i = 0; i < color_block.size.x; i++) {
			for (int j = 0; j < color_block.size.y; j++ ) {
				Log.i("------- BLOCK ------","ERASING mBoard[" + (color_block.origin.x+i) 
						+"]["+ (color_block.origin.y+j)+"]");
				mBoard[color_block.origin.y + i][color_block.origin.x + j] = 0;
			}
		}
		//Log.i("------- BLOCK ------","BLOCK ERASED");
	}

	private void eraseLine(Point origin)
	{
		for (int i = 0; i < BOARD_COLS; i++)
			mBoard[origin.y][i] = 0;
	}

	private void organizeNewBoard(boolean goal, Point origin)
	{
		if (goal) { // The goal was a line
			for (int i = origin.y; i > 0; i--)
				for (int j = 0; j <  BOARD_COLS; j++)
					mBoard[i][j]=mBoard[i-1][j];
		} else { // it was a block
			for (int i = (BOARD_ROWS - 3); i > 0; i--) {
				for (int j = 0; j < BOARD_COLS; j++) {
					int x = i;
					if (mBoard[x][j] != 0) {
						int c = 0;
						while(++x <= (BOARD_ROWS - 2)) {
							if(mBoard[x][j] == 0)
								c++;
							else
								break;
						}
						mBoard[i + c][j] = mBoard[i][j];
						if ( c != 0)
							mBoard[i][j] = 0;
					}
				}
			}
		}
	}
}
